/* 
 * Copyright (c) 2012 JSquirrelEngine
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in 
 * the Software without restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
 * Software, and to permit persons to whom the Software is furnished to do so, subject
 * to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
 * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 * 
 */

package org.jsquirrel.math;


/**
 * 
 * Class representing a closed interval with its lower and upper bounds stored as
 * integer variables.
 * 
 * @author Juan David Adarve Bermudez
 *
 */
public class IInterval {

	
	//*********************************************************************************************
	// INSTANCE ATTRIBUTES
	//*********************************************************************************************
	
	/**
	 * Interval boundaries
	 */
	private int lowerBound, upperBound;	
	
	
	//*********************************************************************************************
	// CONSTRUCTORS
	//*********************************************************************************************
	
	/**
	 * Creates an interval with lower and upper bounds equal to 0.0
	 */
	public IInterval() {
		lowerBound = 0;
		upperBound = 0;
	}
	
	/**
	 * Creates an interval with the given lower and upper bounds. In case the parameters
	 * does not met the condition lowerBound <= upperBound, this constructor makes the
	 * swap between the parameters
	 * 
	 * @param lowerBound: interval lower bound
	 * @param upperBound: interval upper bound
	 */
	public IInterval(int lowerBound, int upperBound) {
		
		if(lowerBound < upperBound) {
			this.lowerBound = lowerBound;
			this.upperBound = upperBound;
		} else
		{
			this.lowerBound = upperBound;
			this.upperBound = lowerBound;
		}
	}
	
	
	//*********************************************************************************************
	// PUBLIC METHODS
	//*********************************************************************************************
	
	/**
	 * @return returns the interval lower bound
	 */
	public final int lowerBound() {
		return lowerBound;
	}
	
	/**
	 * Sets the interval lower bound. This method does not check if the
	 * new value for the lower bound mets the condition lowerBound <= upperBound
	 * 
	 * @param lowerBound: new value for the lower bound
	 */
	public final void setLowerBound(int lowerBound) {
		this.lowerBound = lowerBound;
	}
	
	/**
	 * 
	 * @return returns the interval upper bound
	 */
	public final int upperBound() {
		return upperBound;
	}
	
	/**
	 * Sets the upper bound for this interval. This method does not check
	 * the condition lowerBound <= upperBound
	 * 
	 * @param upperBound: new value for the interval upper bound
	 */
	public final void setUpperBound(int upperBound) {
		this.upperBound = upperBound;
	}	
	
	/**
	 * Checks intersection between this interval and another one give as parameter. Both intervals
	 * are considered as closed intervals. If the parameter is null, the behavior is undefined.
	 * 
	 * @param interval: interval to check intersection against.
	 * 
	 * @return true in case of intersection, false otherwise
	 */
	public final boolean intersects(final IInterval interval) {
		
		boolean cInAB = interval.lowerBound - lowerBound >= 0 && interval.lowerBound - upperBound <= 0;
		boolean dInAB = interval.upperBound - lowerBound >= 0 && interval.upperBound - upperBound <= 0;
		boolean aInCD = lowerBound - interval.lowerBound >= 0 && lowerBound - interval.upperBound <= 0;
		boolean bInCD = upperBound - interval.lowerBound >= 0 && upperBound - interval.upperBound <= 0;
		
		return cInAB || dInAB || aInCD || bInCD;
	}
	
	/**
	 * Checks intersection between this interval and another one given as a two integer variables. Both intervals
	 * are considered as closed intervals.
	 * 
	 * @param lower: lower bound for the other interval
	 * @param upper: upper bound for the other interval
	 * 
	 * @return true in case of intersection, false otherwise
	 */
	public final boolean intersects(int lower, int upper) {
		
		boolean cInAB = lower - lowerBound >= 0 && lower - upperBound <= 0;
		boolean dInAB = upper - lowerBound >= 0 && upper - upperBound <= 0;
		boolean aInCD = lowerBound - lower >= 0 && lowerBound - upper <= 0;
		boolean bInCD = upperBound - lower >= 0 && upperBound - upper <= 0;
		
		return cInAB || dInAB || aInCD || bInCD;
	}
	
	/**
	 * Returns a String with the boundaries of the interval formated as "[l, u]" where l is the lower and u the
	 * upper bound.
	 * 
	 * @return String with the interval information.
	 */
	@Override
	public String toString() {
		return "[" + lowerBound + ", " + upperBound + "]";
	}
	
	
	
	//*********************************************************************************************
	// PUBLIC STATIC METHODS
	//*********************************************************************************************
	
	/**
	 * Check for the intersection of two intervals considered as closed intervals. If any parameter
	 * is null, the behavior is undefined.
	 * 
	 * @param interval1: first interval
	 * @param interval2: second interval
	 * 
	 * @return true in case of intersection, false otherwise
	 */
	public static final boolean intersection(final IInterval interval1, final IInterval interval2) {
		
		boolean cInAB = interval1.lowerBound - interval2.lowerBound >= 0 && interval1.lowerBound - interval2.upperBound <= 0;
		boolean dInAB = interval1.upperBound - interval2.lowerBound >= 0 && interval1.upperBound - interval2.upperBound <= 0;
		boolean aInCD = interval2.lowerBound - interval1.lowerBound >= 0 && interval2.lowerBound - interval1.upperBound <= 0;
		boolean bInCD = interval2.upperBound - interval1.lowerBound >= 0 && interval2.upperBound - interval1.upperBound <= 0;
		
		return cInAB || dInAB || aInCD || bInCD;
	}
	
	/**
	 * Check intersection between two intervals, the first one represented as an IInterval instance and the second one
	 * as two integer for the boundaries. If the interval instance is null or the integer variables do not meet
	 * the condition lower <= upper, then the behavior of this method is undefined
	 * 
	 * @param interval: first interval to check
	 * @param lower: lower bound for the second interval
	 * @param upper: upper bound for the second interval
	 * 
	 * @return true in case of intersection, false otherwise
	 */
	public static final boolean intersection(IInterval interval, int lower, int upper) {
		boolean cInAB = interval.lowerBound - lower >= 0 && interval.lowerBound - upper <= 0;
		boolean dInAB = interval.upperBound - lower >= 0 && interval.upperBound - upper <= 0;
		boolean aInCD = lower - interval.lowerBound >= 0 && lower - interval.upperBound <= 0;
		boolean bInCD = upper - interval.lowerBound >= 0 && upper - interval.upperBound <= 0;
		
		return cInAB || dInAB || aInCD || bInCD;
	}
	
	/**
	 * Computes the intersection of two intervals, represented as two float values.
	 * 
	 * @param lower1: lower bound for the first interval
	 * @param upper1: upper bound for the first interval
	 * @param lower2: lower bound for the second interval
	 * @param upper2: upper bound for the second interval
	 * 
	 * @return true in case of intersection, false otherwise
	 */
	public static final boolean intersection(int lower1, int upper1, int lower2, int upper2) {
		
		boolean cInAB = lower1 - lower2 >= 0 && lower1 - upper2 <= 0;
		boolean dInAB = upper1 - lower2 >= 0 && upper1 - upper2 <= 0;
		boolean aInCD = lower2 - lower1 >= 0 && lower2 - upper1 <= 0;
		boolean bInCD = upper2 - lower1 >= 0 && upper2 - upper1 <= 0;
		
		return cInAB || dInAB || aInCD || bInCD;
	}
}
